﻿

#region Using Statements

// System References
using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using System.Threading;

// XNA References
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;

// Internal References
using Freeform.Framework;

#endregion


namespace Freeform.Framework.Input
{
    /// <summary>
    /// The input manager provides some functionality including input events, storage of old input states, and management of KeyBindings.
    /// </summary>
    public class InputManager : Element
    {

        #region Events

        /// <summary>
        /// This event is fired when any key is pressed.
        /// </summary>
        public event InputEventHandler KeysPressed;


        /// <summary>
        /// This event is fired when any key is released.
        /// </summary>
        public event InputEventHandler KeysReleased;


        /// <summary>
        /// This event is fired when a bind is pressed.
        /// </summary>
        public event BindEventHandler BindPressed;


        /// <summary>
        /// This event is fired when a bind is released.
        /// </summary>
        public event BindEventHandler BindReleased;


        /// <summary>
        /// Represents the signature for handling an input event.
        /// </summary>
        /// <param name="Keys">A list of keys associated with the event.</param>
        public delegate void InputEventHandler(List<Keys> Keys);


        /// <summary>
        /// Represents the signatures for handling bind events.
        /// </summary>
        /// <param name="Bind"></param>
        public delegate void BindEventHandler(KeyBinding Bind);

        #endregion


        #region Members

        /// <summary>
        /// Gets the KeyboardState as of the most recent update.
        /// </summary>
        public KeyboardState KeyState_Current { get; private set; }


        /// <summary>
        /// Gets the KeyboardState of the previous update.
        /// </summary>
        public KeyboardState KeyState_Previous { get; private set; }


        /// <summary>
        /// Gets the MouseState as of the most recent update.
        /// </summary>
        public MouseState MouseState_Current { get; private set; }


        /// <summary>
        /// Gets the MouseState of the previous update.
        /// </summary>
        public MouseState MouseState_Previous { get; private set; }


        /// <summary>
        /// Gets or sets whether text input from the user should be captured.
        /// </summary>
        public Boolean CaptureText { get; set; }

        
        /// <summary>
        /// Gets a dictionary of key bindings.
        /// </summary>
        public Dictionary<String, KeyBinding> Binds { get; set; }


        #region Private Members

        /// <summary>
        /// Gets an array of Keys that are pressed as of the most recent update.
        /// </summary>
        private Keys[] PressedKeys_Current { get; set; }


        /// <summary>
        /// Gets an array of Keys that were pressed last update.
        /// </summary>
        private Keys[] PressedKeys_Previous { get;  set; }


        /// <summary>
        /// Gets the text that has been captured by the input manager since CaptureText has been enabled.
        /// </summary>
        private String Text { get; set; }

        #endregion

        #endregion


        #region Initialisation

        /// <summary>
        /// Creates a new input manager.
        /// </summary>
        public InputManager()
        {
            Text = "";
            CaptureText = false;
            KeyState_Current = Keyboard.GetState();
            KeyState_Previous = KeyState_Current;
            PressedKeys_Current = KeyState_Current.GetPressedKeys();
            PressedKeys_Previous = PressedKeys_Current;
            Binds = new Dictionary<String, KeyBinding>();
        }

        #endregion


        #region Update

        protected override void BeginUpdate(GameTime time)
        {
            base.BeginUpdate(time);

            // Update input state variables.
            KeyState_Previous = KeyState_Current;
            KeyState_Current = Keyboard.GetState(); ;

            MouseState_Previous = MouseState_Current;
            MouseState_Current = Mouse.GetState();

            PressedKeys_Previous = PressedKeys_Current;
            PressedKeys_Current = KeyState_Current.GetPressedKeys();

            // Check for freshly pressed and released keys.
            List<Keys> FreshlyPressed = new List<Keys>();
            List<Keys> FreshlyReleased = new List<Keys>();

            // Find pressed keys.
            foreach (Keys K in PressedKeys_Current)
                if (!PressedKeys_Previous.Contains(K))
                    FreshlyPressed.Add(K);

            // Fire pressed event.
            if (FreshlyPressed.Count > 0 && KeysPressed != null)
                KeysPressed(FreshlyPressed);

            // Find released keys.
            foreach (Keys K in PressedKeys_Previous)
                if (!PressedKeys_Current.Contains(K))
                    FreshlyReleased.Add(K);

            // Fire released event.
            if (FreshlyReleased.Count > 0 && KeysReleased != null)
                KeysReleased(FreshlyReleased);

            // Update bind states.
            foreach (KeyBinding Bind in Binds.Values)
            {
                if (Bind.State == KeyState.Down ^ Game.Input.KeyState_Current.IsKeyDown(Bind.Key))
                {
                    if (Bind.State == KeyState.Up)
                    {
                        Bind.State = KeyState.Down;
                        if (BindPressed != null) BindPressed(Bind);
                    }
                    else
                    {
                        Bind.State = KeyState.Up;
                        if (BindReleased != null) BindReleased(Bind);
                    }
                }
            }

            // Capture keyboard text input.
            if (CaptureText)
            {
                foreach (Keys K in FreshlyPressed)
                {
                    switch (K)
                    {
                        case Keys.Space: Text += " "; break;
                        //case Keys.Enter: Text += "\n"; break;
                        case Keys.Back: if (Text.Length > 0) Text = Text.Substring(0, Text.Length - 1); else Text = "^$#"; break;
                        case Keys.A: Text += KeyState_Current.IsKeyDown(Keys.LeftShift) ? "A" : "a"; break;
                        case Keys.B: Text += KeyState_Current.IsKeyDown(Keys.LeftShift) ? "B" : "b"; break;
                        case Keys.C: Text += KeyState_Current.IsKeyDown(Keys.LeftShift) ? "C" : "c"; break;
                        case Keys.D: Text += KeyState_Current.IsKeyDown(Keys.LeftShift) ? "D" : "d"; break;
                        case Keys.E: Text += KeyState_Current.IsKeyDown(Keys.LeftShift) ? "E" : "e"; break;
                        case Keys.F: Text += KeyState_Current.IsKeyDown(Keys.LeftShift) ? "F" : "f"; break;
                        case Keys.G: Text += KeyState_Current.IsKeyDown(Keys.LeftShift) ? "G" : "g"; break;
                        case Keys.H: Text += KeyState_Current.IsKeyDown(Keys.LeftShift) ? "H" : "h"; break;
                        case Keys.I: Text += KeyState_Current.IsKeyDown(Keys.LeftShift) ? "I" : "i"; break;
                        case Keys.J: Text += KeyState_Current.IsKeyDown(Keys.LeftShift) ? "J" : "j"; break;
                        case Keys.K: Text += KeyState_Current.IsKeyDown(Keys.LeftShift) ? "K" : "k"; break;
                        case Keys.L: Text += KeyState_Current.IsKeyDown(Keys.LeftShift) ? "L" : "l"; break;
                        case Keys.M: Text += KeyState_Current.IsKeyDown(Keys.LeftShift) ? "M" : "m"; break;
                        case Keys.N: Text += KeyState_Current.IsKeyDown(Keys.LeftShift) ? "N" : "n"; break;
                        case Keys.O: Text += KeyState_Current.IsKeyDown(Keys.LeftShift) ? "O" : "o"; break;
                        case Keys.P: Text += KeyState_Current.IsKeyDown(Keys.LeftShift) ? "P" : "p"; break;
                        case Keys.Q: Text += KeyState_Current.IsKeyDown(Keys.LeftShift) ? "Q" : "q"; break;
                        case Keys.R: Text += KeyState_Current.IsKeyDown(Keys.LeftShift) ? "R" : "r"; break;
                        case Keys.S: Text += KeyState_Current.IsKeyDown(Keys.LeftShift) ? "S" : "s"; break;
                        case Keys.T: Text += KeyState_Current.IsKeyDown(Keys.LeftShift) ? "T" : "t"; break;
                        case Keys.U: Text += KeyState_Current.IsKeyDown(Keys.LeftShift) ? "U" : "u"; break;
                        case Keys.V: Text += KeyState_Current.IsKeyDown(Keys.LeftShift) ? "V" : "v"; break;
                        case Keys.W: Text += KeyState_Current.IsKeyDown(Keys.LeftShift) ? "W" : "w"; break;
                        case Keys.X: Text += KeyState_Current.IsKeyDown(Keys.LeftShift) ? "X" : "x"; break;
                        case Keys.Y: Text += KeyState_Current.IsKeyDown(Keys.LeftShift) ? "Y" : "y"; break;
                        case Keys.Z: Text += KeyState_Current.IsKeyDown(Keys.LeftShift) ? "Z" : "z"; break;
                        case Keys.OemComma: Text += KeyState_Current.IsKeyDown(Keys.LeftShift) ? "<" : ","; break;
                        case Keys.OemPeriod: Text += KeyState_Current.IsKeyDown(Keys.LeftShift) ? ">" : "."; break;
                        case Keys.OemQuestion: Text += KeyState_Current.IsKeyDown(Keys.LeftShift) ? "?" : "/"; break;
                        case Keys.OemBackslash: Text += KeyState_Current.IsKeyDown(Keys.LeftShift) ? "|" : "\\"; break;
                        case Keys.OemQuotes: Text += KeyState_Current.IsKeyDown(Keys.LeftShift) ? "\"" : "'"; break;
                        case Keys.OemSemicolon: Text += KeyState_Current.IsKeyDown(Keys.LeftShift) ? ":" : ";"; break;
                        case Keys.OemMinus: Text += KeyState_Current.IsKeyDown(Keys.LeftShift) ? "_" : "-"; break;
                        case Keys.OemPlus: Text += KeyState_Current.IsKeyDown(Keys.LeftShift) ? "+" : "="; break;
                        case Keys.D0: Text += KeyState_Current.IsKeyDown(Keys.LeftShift) ? ")" : "0"; break;
                        case Keys.D1: Text += KeyState_Current.IsKeyDown(Keys.LeftShift) ? "!" : "1"; break;
                        case Keys.D2: Text += KeyState_Current.IsKeyDown(Keys.LeftShift) ? "@" : "2"; break;
                        case Keys.D3: Text += KeyState_Current.IsKeyDown(Keys.LeftShift) ? "#" : "3"; break;
                        case Keys.D4: Text += KeyState_Current.IsKeyDown(Keys.LeftShift) ? "$" : "4"; break;
                        case Keys.D5: Text += KeyState_Current.IsKeyDown(Keys.LeftShift) ? "%" : "5"; break;
                        case Keys.D6: Text += KeyState_Current.IsKeyDown(Keys.LeftShift) ? "^" : "6"; break;
                        case Keys.D7: Text += KeyState_Current.IsKeyDown(Keys.LeftShift) ? "&" : "7"; break;
                        case Keys.D8: Text += KeyState_Current.IsKeyDown(Keys.LeftShift) ? "*" : "8"; break;
                        case Keys.D9: Text += KeyState_Current.IsKeyDown(Keys.LeftShift) ? "(" : "9"; break;

                        default: break;
                    }
                }
            }
        }

        #endregion


        #region Utility

        public String GetCapturedText()
        {
            return Text;
        }

        public void ResetText()
        {
            Text = "";
        }

        #endregion

    }
}